package com.sites.common.safe;

import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.kit.StrKit;
import com.jfinal.log.Log4jLog;
import com.sites.common.SiteInfo;

/**
 * 防御CSRF攻击
 * 
 * @author zyg
 * 2021年2月6日 下午10:17:37
 */
public class CSRFInterceptor implements Interceptor {
	
	Log4jLog log = Log4jLog.getLog(CSRFInterceptor.class);

	@Override
	public void intercept(Invocation inv) {
		Controller controller = inv.getController();
		/**
		 * 防御CSRF的策略一：验证HTTP Referer字段
		 * 根据 HTTP 协议，在 HTTP 头中有一个字段叫 Referer，它记录了该 HTTP 请求的来源地址
		 */
		String referer = controller.getRequest().getHeader("Referer");//从header中获取Referer
		String domain = SiteInfo.siteUrl;//获取配置中的域名信息
		if(StrKit.notBlank(referer) && StrKit.notBlank(domain)){
			referer = referer.contains("?") ? referer.substring(0, referer.indexOf("?")) : referer;//去掉请求里面的参数
			if(!referer.contains(domain)){
				log.error("请求来源验证不通过！");
				controller.renderError(500);
				return;
			}
		}
		
		/**
		 * 防御CSRF的策略二：验证表单中的token
		 */
		String methodName = inv.getMethodName();
		//进入表单页面的方法中创建token
		if(methodName.startsWith("enter")){
			controller.createToken(SiteInfo.formTokenKey, 1800);//有效期30分钟
		}
		//保存数据或更新数据的方法验证token
		if(methodName.startsWith("save") || methodName.startsWith("update")){
			boolean flag = controller.validateToken(SiteInfo.formTokenKey);
			if(!flag){
				log.error(methodName + "：token验证不通过！");
				controller.renderError(500);
				return;
			}
		}
		
		//通过验证，继续执行后面的请求
		inv.invoke();
	}

}
